package pl.edu.agh.ros.node;

import java.util.UUID;

import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.ros.exception.RemoteException;
import org.ros.node.DefaultNodeFactory;
import org.ros.node.NodeConfiguration;
import org.ros.node.service.ServiceClient;
import org.ros.node.service.ServiceResponseListener;

import pl.edu.agh.ros.node.conf.ProxyNodeConfiguration;
import pl.edu.agh.ros.node.conf.XMPPConfiguration;
import pl.edu.agh.ros.node.manager.IServerManager;
import pl.edu.agh.ros.node.manager.XMPPManager;
import pl.edu.agh.ros.node.prop.Props;
import pl.edu.agh.ros.node.util.RosServiceUtils;

import com.google.common.base.Preconditions;

public class ProxyServiceClient<Q extends org.ros.message.Message, A extends org.ros.message.Message> extends
		AbstractProxyNode {

	private final RosServiceUtils<Q, A> rosServiceUtils;
	private ServiceClient<Q, A> serviceClient;
	private String loggedUser;

	public ProxyServiceClient(IServerManager serverManager, XMPPConfiguration xmppConfiguration,
			ProxyNodeConfiguration proxyNodeConfiguration, String nodeName) {
		super(serverManager, xmppConfiguration, proxyNodeConfiguration, nodeName);
		this.rosServiceUtils = new RosServiceUtils<Q, A>();
	}

	public void main(NodeConfiguration configuration) {
		Preconditions.checkState(node == null);
		Preconditions.checkNotNull(configuration);

		try {
			xmppConnector = new XMPPManager();
			xmppConnector.connectAndLogin(xmppConfiguration);
			loggedUser = xmppConnector.getConnection().getUser().split("/")[0]; //$NON-NLS-1$
			node = new DefaultNodeFactory().newNode(nodeName, configuration);
			serviceClient = node.newServiceClient(proxyNodeConfiguration.getName(), proxyNodeConfiguration.getType());
			createXMPPPacketListener();
		} catch (Exception e) {
			if (node != null) {
				node.getLog().fatal(e);
			} else {
				e.printStackTrace();
			}
		}
	}

	private void onXMPPMessage(Message message) {
		if (message.getProperty(Props.XMPP_MSG_TYPE).equals(proxyNodeConfiguration.getType())) {
			Q request = rosServiceUtils.deserializeServiceRequest(message, node, proxyNodeConfiguration.getType());
			UUID uuid = (UUID) message.getProperty(Props.XMPP_MSG_UUID);
			String sender = message.getFrom();
			callRosService(request, uuid, sender);
		}
	}

	private void createXMPPPacketListener() {
		PacketListener packetListener = new PacketListener() {
			public void processPacket(Packet packet) {
				if (packet instanceof Message) {
					Message message = (Message) packet;
					onXMPPMessage(message);
				}
			}
		};
		xmppConnector.getConnection().addPacketListener(packetListener, null);
	}

	private void callRosService(Q request, final UUID uuid, final String sender) {
		serviceClient.call(request, new ServiceResponseListener<A>() {
			@Override
			public void onSuccess(A response) {
				sendServiceResponse(response, uuid, sender);
			}

			@Override
			public void onFailure(RemoteException e) {
				sendServiceException(e, uuid, sender);
			}
		});
	}

	private void sendServiceResponse(A response, UUID uuid, String sender) {
		Message xmppMessage = rosServiceUtils
				.serializeServiceResponse(response, node, proxyNodeConfiguration.getType());
		xmppMessage.setProperty(Props.XMPP_MSG_UUID, uuid);
		xmppMessage.setFrom(loggedUser);
		xmppConnector.sendMessage(xmppMessage, sender);
	}

	private void sendServiceException(RemoteException e, UUID uuid, String sender) {
		Message xmppMessage = new Message();
		xmppMessage.setProperty(Props.XMPP_MSG_UUID, uuid);
		xmppMessage.setFrom(loggedUser);
		xmppMessage.setProperty(Props.XMPP_MSG_EXCEPTION, e);
		xmppConnector.sendMessage(xmppMessage, sender);
	}

}